﻿using Microsoft.Extensions.Logging;
using MQTT.Share.ConfigurationExtension;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Extensions.ManagedClient;
using MQTTnet.Protocol;
using Newtonsoft.Json;
using Polly;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace MQTT.Client.App
{
    public class MqttClientManager : IMqttClientManager
    {
        private Guid ClientId { get; set; }
        public IManagedMqttClient MqttClient { get; set; }
        private readonly ILogger<MqttClientManager> _logger;
        private readonly IConfigurationManager _configurationManager;
        public MqttClientManager(ILogger<MqttClientManager> logger,
            IConfigurationManager configurationManager)
        {
            _logger = logger;
            MqttClient = new MqttFactory().CreateManagedMqttClient();
            _configurationManager = configurationManager;
        }

        public async Task StartAsync()
        {
            try
            {
                string userName = _configurationManager.Configuration["Mqtt:UserName"];
                string password = _configurationManager.Configuration["Mqtt:Password"];
                string host = _configurationManager.Configuration["Mqtt:ServerHost"];
                int port = _configurationManager.GetConfigInt("Mqtt:ServerPort");

                var clientOption = new MqttClientOptionsBuilder()
                    .WithKeepAliveSendInterval(TimeSpan.FromMilliseconds(5000))
                    .WithCredentials(userName, password)
                    .WithClientId((ClientId = Guid.NewGuid()).ToString())
                    .WithTcpServer(host, port)
                    .WithWebSocketServer("http://localhost:5000/mqtt")
                    .WithCommunicationTimeout(TimeSpan.FromSeconds(5))
                    .Build();
                var managedClientOption = new ManagedMqttClientOptionsBuilder()
                    .WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
                    .WithClientOptions(clientOption)
                    .Build();


                MqttClient.ApplicationMessageReceived += MqttClientOnApplicationMessageReceived;
                MqttClient.Disconnected += async (sender, args) =>
                {
                    _logger.LogTrace("MQTT连接断开");
                    await Task.Delay(TimeSpan.FromSeconds(5));
                    _logger.LogTrace("MQTT开始尝试重新连接");
                    await ConnectServer(managedClientOption);
                };
                MqttClient.Connected += (sender, args) =>
                {
                    _logger.LogTrace("MQTT服务连接成功！");
                    var message = new MqttApplicationMessageBuilder()
                        .WithTopic("client/test")
                        .WithPayload(JsonConvert.SerializeObject(new { data = "test" }))
                        .WithAtLeastOnceQoS()
                        //.WithAtMostOnceQoS()
                        .WithRetainFlag()
                        .Build();
                    MqttClient.PublishAsync(message);
                };
                await ConnectServer(managedClientOption);

            }
            catch (Exception e)
            {
                _logger.LogTrace("客户端启动失败:" + e.StackTrace);
            }
        }

        public async Task StopAsync()
        {
            if (MqttClient != null && MqttClient.IsConnected)
            {
                await MqttClient.StopAsync();
                MqttClient?.Dispose();
            }
        }

        private async Task ConnectServer(IManagedMqttClientOptions clientOption)
        {
            if (!MqttClient.IsStarted)
            {
                await Policy
                .Handle<Exception>()
                .RetryAsync(50,
                    (exception, retryCount, context) =>
                    {
                        _logger.LogInformation($"mqtt连接服务器失败，重试次数【{retryCount}】");
                        _logger.LogInformation($"异常信息:", exception.Message);
                    })
                .ExecuteAsync(async () => await MqttClient.StartAsync(clientOption));
                await Subscribe();
            }
                       
        }


        private void MqttClientOnApplicationMessageReceived(object sender,
            MqttApplicationMessageReceivedEventArgs e)
        {
            //消息处理
        }

        private async Task Subscribe()
        {
            await MqttClient.SubscribeAsync(new List<TopicFilter>
            {
                new TopicFilter("server/test", MqttQualityOfServiceLevel.AtLeastOnce),
            });
        }

        public async Task PublishAsync(MqttApplicationMessage message)
        {
            if (MqttClient != null)
            {
                await MqttClient.PublishAsync(message);
            }
        }

        public async Task PublishAsync(string topic, object message)
        {
            if (MqttClient != null)
            {
                var data = new MqttApplicationMessageBuilder()
                        .WithTopic(topic)
                        .WithPayload(JsonConvert.SerializeObject(message))
                        .WithAtLeastOnceQoS()
                        .WithRetainFlag()
                        .Build();
                await MqttClient.PublishAsync(data);
            }
        }
    }
}
